package debug

import (
	"fmt"
	"time"
	"strconv"
	"kumachan/standalone/ctn"
	"kumachan/standalone/util/richtext"
	"kumachan/interpreter/core"
	"kumachan/interpreter/compiler"
	"kumachan/lang/source"
	"kumachan/lang/typsys"
)


// TODO: change to an interface
type InspectContext struct {
	context  *compiler.NsHeaderMap
}
func MakeInspectContext(ctx *compiler.NsHeaderMap) InspectContext {
	return InspectContext { ctx }
}
func (ctx InspectContext) FindType(ref source.Ref) (*typsys.TypeDef, bool) {
	return ctx.context.FindType(ref)
}

// TODO: rewrite this function (and move to core)
func Inspect(v core.Object, t typsys.Type, ctx InspectContext) richtext.Block {
	var vague = func(t_keyword string, v core.Object) richtext.Block {
		var b richtext.Block
		b.WriteSpan("{", richtext.TAG_SRC_KEYWORD)
		b.WriteSpan(t_keyword, richtext.TAG_SRC_KEYWORD)
		b.WriteSpan(InspectUntypedValue(v), richtext.TAG_SRC_NORMAL)
		b.WriteSpan("}", richtext.TAG_SRC_KEYWORD)
		return b
	}
	var constant = func(t typsys.Type, v interface{}) richtext.Block {
		var b richtext.Block
		b.WriteSpan("{", richtext.TAG_SRC_KEYWORD)
		b.WriteSpan(typsys.Describe(t), richtext.TAG_SRC_TYPE)
		b.WriteSpan(fmt.Sprint(v), richtext.TAG_SRC_NORMAL)
		b.WriteSpan("}", richtext.TAG_SRC_KEYWORD)
		return b
	}
	var write_type_line = func(b *richtext.Block) {
		var t_desc = typsys.Describe(t)
		b.WriteLine(t_desc, richtext.TAG_SRC_TYPE)
	}
	if v == nil {
		var b richtext.Block
		b.WriteLine("None")
		return b
	}
	if t == nil {
		return vague("?", v)
	}
	switch T := t.(type) {
	case typsys.InferringType:
		return vague(T.Id, v)
	case typsys.ParameterType:
		return vague(T.Name, v)
	case typsys.RefType:
		var n = T.Def
		if n.Namespace == "" {
			switch n.ItemName {
			case core.T_Bool:
				if core.GetBool(v) {
					return constant(t, core.TrueString)
				} else {
					return constant(t, core.FalseString)
				}
			case core.T_Char:
				var char = (*v).(core.Char)
				var char_str = string([] rune { rune(char) })
				var char_desc = fmt.Sprintf("`%s` (0x%X)", char_str, char)
				return constant(t, char_desc)
			case core.T_String:
				var str = (*v).(core.String)
				var str_desc = strconv.Quote(string(str))
				return constant(t, str_desc)
			case core.T_Bytes:
				var bin = (*v).(core.Bytes)
				var bin_desc = fmt.Sprintf("%+v", bin)
				return constant(t, bin_desc)
			case core.T_Int:
				return constant(t, (*v).(core.Int).Value.String())
			case core.T_Float:
				return constant(t, float64((*v).(core.Float)))
			case core.T_Time:
				return constant(t, time.Time((*v).(core.Time)))
			case core.T_Error:
				return constant(t, strconv.Quote((*v).(core.Error).Value.Error()))
			case core.T_Lambda:
				var b richtext.Block
				b.WriteLine(typsys.Describe(t), richtext.TAG_SRC_TYPE)
				var child richtext.Block
				child.WriteSpan(InspectUntypedValue(v), richtext.TAG_SRC_NORMAL)
				b.Append(child)
				return b
			case core.T_List:
				var b richtext.Block
				b.WriteLine(typsys.Describe(t), richtext.TAG_SRC_TYPE)
				(*v).(core.List).ForEach(func(item core.Object) {
					b.Append(Inspect(item, T.Args[0], ctx))
				})
				return b
			case core.T_Queue:
				var b richtext.Block
				b.WriteLine(typsys.Describe(t), richtext.TAG_SRC_TYPE)
				var queue = ctn.Queue[core.Object]((*v).(core.Queue))
				queue.ForEach(func(item core.Object) {
					b.Append(Inspect(item, T.Args[0], ctx))
				})
				return b
			case core.T_Heap:
				var b richtext.Block
				b.WriteLine(typsys.Describe(t), richtext.TAG_SRC_TYPE)
				var heap = ctn.Heap[core.Object]((*v).(core.Heap))
				heap.ForEach(func(item core.Object) {
					b.Append(Inspect(item, T.Args[0], ctx))
				})
				return b
			case core.T_Set:
				var b richtext.Block
				b.WriteLine(typsys.Describe(t), richtext.TAG_SRC_TYPE)
				var set = ctn.Set[core.Object]((*v).(core.Set))
				set.ForEach(func(item core.Object) {
					b.Append(Inspect(item, T.Args[0], ctx))
				})
				return b
			case core.T_Map:
				var b richtext.Block
				b.WriteLine(typsys.Describe(t), richtext.TAG_SRC_TYPE)
				var map_ = ctn.Map[core.Object,core.Object]((*v).(core.Map))
				map_.ForEach(func(key core.Object, val core.Object) {
					var item_msg richtext.Block
					item_msg.Append(Inspect(key, T.Args[0], ctx))
					item_msg.Append(Inspect(val, T.Args[1], ctx))
					b.Append(item_msg)
				})
				return b
			}
		}
		var def, ok = ctx.FindType(n)
		if ok {
			switch content := def.Content.(type) {
			case typsys.Record:
				var V = (*v).(core.Record)
				var b richtext.Block
				write_type_line(&b)
				for i, field := range content.FieldList {
					var field_t = typsys.Inflate(
						field.Type, def.Parameters, T.Args,
					)
					var child richtext.Block
					child.WriteSpan(field.Name, richtext.TAG_SRC_NAME)
					child.WriteSpan(":", richtext.TAG_SRC_NORMAL)
					child.Append(Inspect(V.Objects[i], field_t, ctx))
					b.Append(child)
				}
				return b
			case typsys.Union:
				var u = (*v).(core.Union)
				var b richtext.Block
				write_type_line(&b)
				var item = content.FieldList[u.Index]
				var case_t = typsys.Inflate(item.Type, def.Parameters, T.Args)
				var inner = Inspect(u.Object, case_t, ctx)
				b.Append(inner)
				return b
			case typsys.Enum:
				var index = int((*v).(core.Enum))
				var item = content.FieldList[index]
				var b richtext.Block
				write_type_line(&b)
				var item_desc = fmt.Sprintf("%s(%d)", item.Name, index)
				b.WriteLine(item_desc, richtext.TAG_SRC_NORMAL)
				return b
			}
		}
		var b richtext.Block
		write_type_line(&b)
		b.WriteLine(InspectUntypedValue(v), richtext.TAG_SRC_NORMAL)
		return b
	}
	return vague("?", v)
}

func InspectUntypedValue(v core.Object) string {
	if _, is_interface := (*v).(core.Interface); is_interface {
		return "interface"
	}
	if _, is_lambda := (*v).(core.Lambda); is_lambda {
		return "lambda"
	}
	return fmt.Sprintf("%+v", v)
}


